package client

import scala.actors._
import java.net._
import assignments._
import scala.collection.mutable._

/**
 * Client scheduler that keeps track of current assignment and schedules tasks
 * @author Jacob Biggs
 *
 */
class Scheduler (connector : Connector) extends Actor {

	var status : Status = Idle
	var assignment : Assignment = NoAssignment
	val workers : ListBuffer[ ClientWorker ] = new ListBuffer[ ClientWorker ]
	
	def act()
	{
	  // Create workers for all available cores
	  for(i <- List.range(0, Runtime.getRuntime().availableProcessors())){
	    val worker = new ClientWorker(i)
	    worker.start()
	    workers += worker
	  }
	  
		loop
		{
			react
			{
				case messages.StatusRequest() =>
					handleStatusRequest()
					
				case messages.Notified =>
					println("Server notified of client activation")
					
					// Check if client is idle
					if (status == Idle)
					{
  					requestAssignment()
					}
          
				case messages.AssignmentUnavailable =>
				  println("No assignments available on server.. sleeping..")
				  
				  // Wait 15 minutes and request a new assignment
				  Thread.sleep(1000 * 60 * 15)
				  requestAssignment()
					
				case newAssignment : Assignment =>
					println("New assignment recieved from server")
					assignment = newAssignment
					activeNewAssignment()
					
				case result : ProjectResult =>
					// Send a new task to the worker
          sendTask(sender)
					
					// Set task as complete
          assignment.tasks.find(task => task.id == result.taskId).get.complete = true
					
          // Add result to results list
          assignment.results += result
          
					println("Task pool " + assignment.progress + "% completed")
					
					if(assignment.progress == 100d)
					{
					  assignment.pack()
					  
					  connector ! assignment.collect()
					}
					
					
			}
		}
	}
	
	/**
	 * 
	 * Send new task to worker that sent result
	 * 
	 */
	def sendTask(worker : OutputChannel[Any])
	{
	  // Get an unassigned task
      val option = assignment.tasks.find(task => task.assigned == false)
      
      option match{
        case Some(task) =>
          // Assign the task
          task.assigned = true
          
          // Send to worker
          worker ! task
        case None =>
          worker ! Idle
      }
      
      
	}
	
	def requestAssignment()
	{
	  // Request new assignment from server
    connector ! messages.RequestAssignment(0)
    println("Requesting new assignment...")
	}
	
	def activeNewAssignment()
	{
	  // Send an unassigned task to each worker
	  for(worker <- workers)
	  {
	    sendTask(worker)
	  }
	}
	
	def handleStatusRequest()
	{
	  println("Server requested status")
	  status match
      {
        case Idle =>
          sender ! messages.Idle(InetAddress.getLocalHost())
          println("Sent Idle message to server")
        case Busy =>
          sender ! messages.Busy(InetAddress.getLocalHost())
          println("Sent Busy message to server")
      }
	}
	
}